From 396acf3bbbe00a192cb0ea0a9ccf91b1d8d2850b Mon Sep 17 00:00:00 2001 From: Fuwn <50817549+Fuwn@users.noreply.github.com> Date: Sat, 24 Jan 2026 13:09:50 +0000 Subject: Initial commit Created from https://vercel.com/new --- .../[websiteId]/cohorts/CohortAddButton.tsx | 21 ++++ .../[websiteId]/cohorts/CohortDeleteButton.tsx | 60 +++++++++ .../[websiteId]/cohorts/CohortEditButton.tsx | 37 ++++++ .../[websiteId]/cohorts/CohortEditForm.tsx | 135 +++++++++++++++++++++ .../[websiteId]/cohorts/CohortsDataTable.tsx | 24 ++++ .../websites/[websiteId]/cohorts/CohortsPage.tsx | 16 +++ .../websites/[websiteId]/cohorts/CohortsTable.tsx | 41 +++++++ .../(main)/websites/[websiteId]/cohorts/page.tsx | 12 ++ 8 files changed, 346 insertions(+) create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx create mode 100644 src/app/(main)/websites/[websiteId]/cohorts/page.tsx (limited to 'src/app/(main)/websites/[websiteId]/cohorts') diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx new file mode 100644 index 0000000..3f7f872 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortAddButton.tsx @@ -0,0 +1,21 @@ +import { useMessages } from '@/components/hooks'; +import { Plus } from '@/components/icons'; +import { DialogButton } from '@/components/input/DialogButton'; +import { CohortEditForm } from './CohortEditForm'; + +export function CohortAddButton({ websiteId }: { websiteId: string }) { + const { formatMessage, labels } = useMessages(); + + return ( + } + label={formatMessage(labels.cohort)} + variant="primary" + width="800px" + > + {({ close }) => { + return ; + }} + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx new file mode 100644 index 0000000..94d62ff --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton.tsx @@ -0,0 +1,60 @@ +import { ConfirmationForm } from '@/components/common/ConfirmationForm'; +import { useDeleteQuery, useMessages } from '@/components/hooks'; +import { Trash } from '@/components/icons'; +import { DialogButton } from '@/components/input/DialogButton'; +import { messages } from '@/components/messages'; + +export function CohortDeleteButton({ + cohortId, + websiteId, + name, + onSave, +}: { + cohortId: string; + websiteId: string; + name: string; + onSave?: () => void; +}) { + const { formatMessage, labels, FormattedMessage } = useMessages(); + const { mutateAsync, isPending, error, touch } = useDeleteQuery( + `/websites/${websiteId}/segments/${cohortId}`, + ); + + const handleConfirm = async (close: () => void) => { + await mutateAsync(null, { + onSuccess: () => { + touch('cohorts'); + onSave?.(); + close(); + }, + }); + }; + + return ( + } + variant="quiet" + title={formatMessage(labels.confirm)} + width="400px" + > + {({ close }) => ( + {name}, + }} + /> + } + isLoading={isPending} + error={error} + onConfirm={handleConfirm.bind(null, close)} + onClose={close} + buttonLabel={formatMessage(labels.delete)} + buttonVariant="danger" + /> + )} + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx new file mode 100644 index 0000000..0799071 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditButton.tsx @@ -0,0 +1,37 @@ +import { CohortEditForm } from '@/app/(main)/websites/[websiteId]/cohorts/CohortEditForm'; +import { useMessages } from '@/components/hooks'; +import { Edit } from '@/components/icons'; +import { DialogButton } from '@/components/input/DialogButton'; +import type { Filter } from '@/lib/types'; + +export function CohortEditButton({ + cohortId, + websiteId, + filters, +}: { + cohortId: string; + websiteId: string; + filters: Filter[]; +}) { + const { formatMessage, labels } = useMessages(); + + return ( + } + variant="quiet" + title={formatMessage(labels.cohort)} + width="800px" + > + {({ close }) => { + return ( + + ); + }} + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx new file mode 100644 index 0000000..c755035 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortEditForm.tsx @@ -0,0 +1,135 @@ +import { + Button, + Column, + Form, + FormButtons, + FormField, + FormSubmitButton, + Grid, + Label, + Loading, + TextField, +} from '@umami/react-zen'; +import { useMessages, useUpdateQuery, useWebsiteCohortQuery } from '@/components/hooks'; +import { ActionSelect } from '@/components/input/ActionSelect'; +import { DateFilter } from '@/components/input/DateFilter'; +import { FieldFilters } from '@/components/input/FieldFilters'; +import { LookupField } from '@/components/input/LookupField'; + +export function CohortEditForm({ + cohortId, + websiteId, + filters = [], + onSave, + onClose, +}: { + cohortId?: string; + websiteId: string; + filters?: any[]; + showFilters?: boolean; + onSave?: () => void; + onClose?: () => void; +}) { + const { data } = useWebsiteCohortQuery(websiteId, cohortId); + const { formatMessage, labels, messages, getErrorMessage } = useMessages(); + + const { mutateAsync, error, isPending, touch, toast } = useUpdateQuery( + `/websites/${websiteId}/segments${cohortId ? `/${cohortId}` : ''}`, + { + type: 'cohort', + }, + ); + + const handleSubmit = async (formData: any) => { + await mutateAsync(formData, { + onSuccess: async () => { + toast(formatMessage(messages.saved)); + touch('cohorts'); + onSave?.(); + onClose?.(); + }, + }); + }; + + if (cohortId && !data) { + return ; + } + + const defaultValues = { + parameters: { filters, dateRange: '30day', action: { type: 'path', value: '' } }, + }; + + return ( +
+ {({ watch }) => { + const type = watch('parameters.action.type'); + + return ( + <> + + + + + + + + + + + + + + + {({ field }) => { + return ; + }} + + + + + + + + + + + + + + + + + + + + + + + {formatMessage(labels.save)} + + + + ); + }} +
+ ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx new file mode 100644 index 0000000..6734384 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsDataTable.tsx @@ -0,0 +1,24 @@ +import { DataGrid } from '@/components/common/DataGrid'; +import { useWebsiteCohortsQuery } from '@/components/hooks'; +import { CohortAddButton } from './CohortAddButton'; +import { CohortsTable } from './CohortsTable'; + +export function CohortsDataTable({ websiteId }: { websiteId?: string }) { + const query = useWebsiteCohortsQuery(websiteId, { type: 'cohort' }); + + const renderActions = () => { + return ; + }; + + return ( + + {({ data }) => } + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx new file mode 100644 index 0000000..14f366e --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsPage.tsx @@ -0,0 +1,16 @@ +'use client'; +import { Column } from '@umami/react-zen'; +import { WebsiteControls } from '@/app/(main)/websites/[websiteId]/WebsiteControls'; +import { Panel } from '@/components/common/Panel'; +import { CohortsDataTable } from './CohortsDataTable'; + +export function CohortsPage({ websiteId }) { + return ( + + + + + + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx b/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx new file mode 100644 index 0000000..5c7ac03 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/CohortsTable.tsx @@ -0,0 +1,41 @@ +import { DataColumn, DataTable, type DataTableProps, Row } from '@umami/react-zen'; +import Link from 'next/link'; +import { CohortDeleteButton } from '@/app/(main)/websites/[websiteId]/cohorts/CohortDeleteButton'; +import { CohortEditButton } from '@/app/(main)/websites/[websiteId]/cohorts/CohortEditButton'; +import { DateDistance } from '@/components/common/DateDistance'; +import { useMessages, useNavigation } from '@/components/hooks'; +import { filtersObjectToArray } from '@/lib/params'; + +export function CohortsTable(props: DataTableProps) { + const { formatMessage, labels } = useMessages(); + const { websiteId, renderUrl } = useNavigation(); + + return ( + + + {(row: any) => ( + {row.name} + )} + + + {(row: any) => } + + + {(row: any) => { + const { id, name, parameters } = row; + + return ( + + + + + ); + }} + + + ); +} diff --git a/src/app/(main)/websites/[websiteId]/cohorts/page.tsx b/src/app/(main)/websites/[websiteId]/cohorts/page.tsx new file mode 100644 index 0000000..9946f60 --- /dev/null +++ b/src/app/(main)/websites/[websiteId]/cohorts/page.tsx @@ -0,0 +1,12 @@ +import type { Metadata } from 'next'; +import { CohortsPage } from './CohortsPage'; + +export default async function ({ params }: { params: Promise<{ websiteId: string }> }) { + const { websiteId } = await params; + + return ; +} + +export const metadata: Metadata = { + title: 'Cohorts', +}; -- cgit v1.2.3